package com.inwavetech.os.tests.sync;


import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;

import com.inwavetech.os.OsDetailActivity;
import com.inwavetech.os.R;
import com.inwavetech.os.entity.Os;
import com.inwavetech.os.entity.Product;
import com.inwavetech.os.entity.User;
import com.inwavetech.os.entity.enummap.OsClosingStatusMap;
import com.inwavetech.os.entity.enummap.OsServiceOrderTypeMap;
import com.inwavetech.os.entity.enummap.OsStatusMap;
import com.inwavetech.os.entity.enummap.ProductDiagnosysMap;
import com.inwavetech.os.sync.OsUpdater;
import com.inwavetech.os.sync.jsonparser.InvalidEntityJsonException;
import com.inwavetech.os.sync.jsonparser.OsJsonParser;
import com.inwavetech.os.sync.jsonparser.ProductJsonParser;
import com.inwavetech.os.tests.sync.mock.OsJson;
import com.inwavetech.os.tests.sync.mock.SimpleResponseAnswer;
import com.inwavetech.os.tests.sync.util.DateHandler;
import com.inwavetech.os.util.DaoHelper;
import com.inwavetech.os.util.SessionManager;
import com.inwavetech.os.util.SimpleResponse;
import com.inwavetech.os.util.request.Get;
import com.j256.ormlite.dao.Dao;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.io.IOException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


/**
 * Created by jgskin on 2/27/14.
 */
public class OsUpdaterTest extends AndroidTestCase {
    private DaoHelper daoHelper;
    private final OsStatusMap statusMap;
    private final OsClosingStatusMap closingStatusMap;
    private final OsServiceOrderTypeMap serviceOrderTypeMap;
    private final ProductDiagnosysMap diagnosysMap;
    private RenamingDelegatingContext context;
    private String baseUrl;
    private OsUpdater updater;
    private User user;
    private SessionManager smMock;
    private DateHandler dateHandler;
    private Get getMock;
    private SimpleResponseAnswer simpleResponseAnswer;

    public OsUpdaterTest() throws SQLException {
        statusMap = new OsStatusMap();
        closingStatusMap = new OsClosingStatusMap();
        serviceOrderTypeMap = new OsServiceOrderTypeMap();
        diagnosysMap = new ProductDiagnosysMap();
    }

    @Override
    protected void setUp() throws Exception {
        context = new RenamingDelegatingContext(getContext(), "test_");

        daoHelper = new DaoHelper(context);

        // the os updater base url for server request
        baseUrl = getContext().getString(R.string.api_get_oses);

        // user
        user = new User();
        user.username = "ausername";
        daoHelper.getDao(User.class).create(user);

        // session manager
        smMock = Mockito.mock(SessionManager.class);
        Mockito.when(smMock.getUser()).thenReturn(user);

        // data handling
        dateHandler = new DateHandler();

        // the request
        getMock = Mockito.mock(Get.class);
        simpleResponseAnswer = new SimpleResponseAnswer();
        Mockito.when(getMock.execute(Mockito.anyString())).thenAnswer(simpleResponseAnswer);

        // updater
        updater = new OsUpdater(
                daoHelper,
                smMock,
                getMock,
                dateHandler.getDateFormatter(),
                new OsJsonParser(statusMap, closingStatusMap, serviceOrderTypeMap, dateHandler.getDateFormatter()),
                new ProductJsonParser(diagnosysMap, dateHandler.getDateFormatter()),
                baseUrl);
    }

    public void testUrlFormat_noDateParam() throws Exception {
        try {
            executeUpdater();
        } catch (JSONException e) {}

        Mockito.verify(getMock).execute(String.format(baseUrl, "1"));
    }

    public void testUrlFormat_dataParam() throws Exception {
        user.last_updated_at_data = dateHandler.getDate();

        try {
            executeUpdater();
        } catch (JSONException e) {}

        Mockito.verify(getMock).execute(
                String.format("%s&updated_at=%s", String.format(baseUrl, "1"), dateHandler.getDateStr()));
    }

    public void testServerPageFlipping() throws JSONException, IOException, SessionManager.SessionManagerException, InvalidEntityJsonException, SQLException {
        // 3 pages
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(0, 2).toString()));
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(2, 2).toString()));
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(4, 2).toString()));

        executeUpdater();

        ArgumentCaptor<String> argCap = ArgumentCaptor.forClass(String.class);
        Mockito.verify(getMock, Mockito.times(4)).execute(argCap.capture());

        List<String> args = argCap.getAllValues();
        assertEquals(String.format(baseUrl, 1), args.get(0));
        assertEquals(String.format(baseUrl, 2), args.get(1));
        assertEquals(String.format(baseUrl, 3), args.get(2));
        assertEquals(String.format(baseUrl, 4), args.get(3));
    }

    public void testUpdateUserLastSync() throws Exception {
        // some stub data
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(0, 2).toString()));
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(2, 2).toString()));

        // lets change the last date for the last received data
        JSONArray lastResponseData = buildJsonResponseData(4, 2);
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_MONTH, 1);
        String tomorrowStr = dateHandler.getDateFormatter().format(c.getTime());
        lastResponseData.getJSONObject(1).put("updated_at", tomorrowStr);
        simpleResponseAnswer.add(new SimpleResponse(200, lastResponseData.toString()));

        executeUpdater();

        assertEquals(tomorrowStr, dateHandler.getDateFormatter().format(
                ((Dao<User, String>) daoHelper.getDao(User.class)).queryForId(user.username).last_updated_at_data));
    }

    public void testInsertAndUpdate() throws JSONException, IOException, SessionManager.SessionManagerException, SQLException, InvalidEntityJsonException, ParseException {
        // lets begin with what we expect
        int firstInserts = 3;
        int secondInserts = 2;
        int expectedInserts = firstInserts + secondInserts;

        // create the base data for the json response
        JSONArray jsonColl = buildJsonResponseData(0, firstInserts);

        /*
        We will insert and after update an os. To assert that it will work lets observe some values.
        */
        JSONObject secondOsJson = jsonColl.getJSONObject(1);
        secondOsJson.put("status", statusMap.getId(Os.Status.ACTIVE));
        JSONObject secondOsJsonFirstProduct = secondOsJson.getJSONArray("products").getJSONObject(0);
        secondOsJsonFirstProduct.put("diagnostic_detail", "abc");

        // inject the json on the request response
        simpleResponseAnswer.add(new SimpleResponse(200, jsonColl.toString()));
        simpleResponseAnswer.add(new SimpleResponse(200, buildJsonResponseData(firstInserts, secondInserts).toString()));

        // run the updater
        executeUpdater();

        // lets check the newly created os's
        List<Os> osCollection = daoHelper.getDao(Os.class).queryForAll();

        // the response had data for 3 os creation
        assertEquals(expectedInserts, osCollection.size());

        for (Os os: osCollection) {
            // check the second os value
            if (os.original_id.equals(secondOsJson.get("id"))) {
                assertEquals(Os.Status.ACTIVE, os.status);

                // now check the product value
                for (Product product: os.products) {
                    if (product.original_id.equals(secondOsJsonFirstProduct.get("id"))) {
                        assertEquals(product.diagnosys_detail, "abc");
                    }
                }
            }

            // each os json data contains 3 products data
            assertEquals(3, os.products.size());
        }

        // test update
        // lets change a json entry
        secondOsJson.put("status_id", statusMap.getId(Os.Status.FINISHED));
        secondOsJsonFirstProduct.put("diagnostic_detail", "cba");
        Date newdate = (Date) dateHandler.getDate().clone();
        newdate.setTime(dateHandler.getDate().getTime() + 30000);
        secondOsJson.put("updated_at", dateHandler.getDateFormatter().format(newdate));

        // inject in the response
        simpleResponseAnswer.reset();
        simpleResponseAnswer.get(0).body = jsonColl.toString();

        // run the updater
        executeUpdater();

        // check the db again
        osCollection = daoHelper.getDao(Os.class).queryForAll();

        // no changes. ty.
        assertEquals(expectedInserts, osCollection.size());

        for (Os os: osCollection) {
            // check the updated values
            if (os.original_id.equals(secondOsJson.get("id"))) {
                assertEquals(Os.Status.FINISHED, os.status);

                // now check the product value
                for (Product product: os.products) {
                    if (product.original_id.equals(secondOsJsonFirstProduct.get("id"))) {
                        assertEquals("cba", product.diagnosys_detail);
                    }
                }
            }

            // no changes here too
            assertEquals(3, os.products.size());
        }
    }

    public void testAtomicImport() throws Exception {
        JSONArray coll = buildJsonResponseData(0, 10);

        for (int i=0; i<coll.length(); i++) {
            if (i == 1 || i == 8) {
                coll.getJSONObject(i).put("status_id", "ainvalidvalue");
            }
        }

        simpleResponseAnswer.add(new SimpleResponse(200, coll.toString()));

        executeUpdater();

        assertEquals(8, daoHelper.getDao(Os.class).countOf());
    }

    private JSONArray buildJsonResponseData(int ini, int limit) throws JSONException {
        JSONArray jsonColl = new JSONArray();
        for (int i = ini; i < limit + ini; i++) {
            jsonColl.put(new OsJson(i, statusMap, closingStatusMap,
                    serviceOrderTypeMap, diagnosysMap, dateHandler));
        }
        return jsonColl;
    }

    private void executeUpdater() throws IOException, SessionManager.SessionManagerException, InvalidEntityJsonException, JSONException, SQLException {
        updater.execute();
    }
}
